#!/usr/bin/env node

const { spawn } = require('child_process');
const path = require('path');

/**
 * Test runner script with different modes
 */
class TestRunner {
  constructor() {
    this.projectRoot = path.resolve(__dirname, '..');
  }

  /**
   * Run command with proper error handling
   */
  async runCommand(command, args = [], options = {}) {
    return new Promise((resolve, reject) => {
      console.log(`\n🚀 Running: ${command} ${args.join(' ')}`);
      
      const child = spawn(command, args, {
        cwd: this.projectRoot,
        stdio: 'inherit',
        shell: true,
        ...options
      });

      child.on('close', (code) => {
        if (code === 0) {
          resolve(code);
        } else {
          reject(new Error(`Command failed with exit code ${code}`));
        }
      });

      child.on('error', (error) => {
        reject(error);
      });
    });
  }

  /**
   * Run unit tests only
   */
  async runUnitTests() {
    console.log('\n📋 Running Unit Tests...');
    console.log('================================');
    
    try {
      await this.runCommand('npm', ['run', 'test:unit']);
      console.log('\n✅ Unit tests completed successfully!');
    } catch (error) {
      console.error('\n❌ Unit tests failed:', error.message);
      throw error;
    }
  }

  /**
   * Run integration tests only
   */
  async runIntegrationTests() {
    console.log('\n🔗 Running Integration Tests...');
    console.log('================================');
    
    try {
      await this.runCommand('npm', ['run', 'test:integration']);
      console.log('\n✅ Integration tests completed successfully!');
    } catch (error) {
      console.error('\n❌ Integration tests failed:', error.message);
      throw error;
    }
  }

  /**
   * Run all tests
   */
  async runAllTests() {
    console.log('\n🧪 Running All Tests...');
    console.log('================================');
    
    try {
      await this.runCommand('npm', ['test']);
      console.log('\n✅ All tests completed successfully!');
    } catch (error) {
      console.error('\n❌ Some tests failed:', error.message);
      throw error;
    }
  }

  /**
   * Run tests with coverage
   */
  async runTestsWithCoverage() {
    console.log('\n📊 Running Tests with Coverage...');
    console.log('================================');
    
    try {
      await this.runCommand('npm', ['run', 'test:coverage']);
      console.log('\n✅ Tests with coverage completed successfully!');
    } catch (error) {
      console.error('\n❌ Tests with coverage failed:', error.message);
      throw error;
    }
  }

  /**
   * Run CI tests (unit + integration)
   */
  async runCITests() {
    console.log('\n🤖 Running CI Tests...');
    console.log('================================');
    
    try {
      await this.runCommand('npm', ['run', 'test:ci']);
      console.log('\n✅ CI tests completed successfully!');
    } catch (error) {
      console.error('\n❌ CI tests failed:', error.message);
      throw error;
    }
  }

  /**
   * Check RabbitMQ availability
   */
  async checkRabbitMQ() {
    console.log('\n🐰 Checking RabbitMQ availability...');
    
    try {
      // Run a simple connection test
      await this.runCommand('node', ['-e', `
        const { isRabbitMQAvailable } = require('./tests/test-config');
        isRabbitMQAvailable().then(available => {
          if (available) {
            console.log('✅ RabbitMQ is available');
            process.exit(0);
          } else {
            console.log('❌ RabbitMQ is not available');
            process.exit(1);
          }
        }).catch(error => {
          console.error('❌ Error checking RabbitMQ:', error.message);
          process.exit(1);
        });
      `]);
    } catch (error) {
      console.warn('⚠️ RabbitMQ not available - integration tests will be skipped');
      return false;
    }
    
    return true;
  }

  /**
   * Main execution function
   */
  async run() {
    const args = process.argv.slice(2);
    const mode = args[0] || 'help';

    console.log('🧪 RabbitMQ Test Runner');
    console.log('========================');

    try {
      switch (mode) {
        case 'unit':
          await this.runUnitTests();
          break;
          
        case 'integration':
          const rabbitMQAvailable = await this.checkRabbitMQ();
          if (rabbitMQAvailable) {
            await this.runIntegrationTests();
          } else {
            console.log('⚠️ Skipping integration tests - RabbitMQ not available');
          }
          break;
          
        case 'all':
          await this.runAllTests();
          break;
          
        case 'coverage':
          await this.runTestsWithCoverage();
          break;
          
        case 'ci':
          await this.runCITests();
          break;
          
        case 'check':
          await this.checkRabbitMQ();
          break;
          
        case 'help':
        default:
          this.showHelp();
          break;
      }
    } catch (error) {
      console.error('\n💥 Test execution failed:', error.message);
      process.exit(1);
    }
  }

  /**
   * Show help information
   */
  showHelp() {
    console.log(`
Usage: node scripts/run-tests.js [mode]

Modes:
  unit         Run unit tests only (fast, no external dependencies)
  integration  Run integration tests only (requires RabbitMQ)
  all          Run all tests
  coverage     Run tests with coverage report
  ci           Run CI tests (unit + integration)
  check        Check RabbitMQ availability
  help         Show this help message

Examples:
  node scripts/run-tests.js unit
  node scripts/run-tests.js integration
  node scripts/run-tests.js coverage

Environment Variables:
  TEST_MODE=unit|integration|e2e    Set test mode
  RUN_INTEGRATION_TESTS=true        Force integration tests
  VERBOSE_TESTS=true                Enable verbose output
  CI=true                           Enable CI mode
`);
  }
}

// Run the test runner
if (require.main === module) {
  const runner = new TestRunner();
  runner.run().catch(error => {
    console.error('Fatal error:', error);
    process.exit(1);
  });
}

module.exports = TestRunner;
